#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/types.h>

#include "ble_transport_svc.h"

LOG_MODULE_REGISTER(ble_transport, LOG_LEVEL_DBG);

static struct ble_transport_cb *callbacks;

static void on_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
    if (callbacks->ccc_changed)
    {
        callbacks->ccc_changed((value == BT_GATT_CCC_NOTIFY) ? 0x0001 : 0x0000);
    }
}

static ssize_t on_received(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len,
                           uint16_t offset, uint8_t flags)
{
    LOG_DBG("Received data, handle %d, conn %p, len: %d", attr->handle, (void *)conn, len);

    if (callbacks->received)
    {
        callbacks->received(conn, buf, len);
    }
    return len;
}

BT_GATT_SERVICE_DEFINE(trans_svc,
                       BT_GATT_PRIMARY_SERVICE(BLE_TRANSPORT_UUID_SERVICE),
                       BT_GATT_CHARACTERISTIC(BLE_TRANSPORT_UUID_TX,
                                              BT_GATT_CHRC_NOTIFY,
                                              BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
                                              NULL, NULL, NULL),
                       BT_GATT_CCC(on_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
                       BT_GATT_CHARACTERISTIC(BLE_TRANSPORT_UUID_RX,
                                              BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP,
                                              BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
                                              NULL, on_received, NULL));

int ble_transport_svc_init(struct ble_transport_cb *cb)
{
    if (cb)
    {
        callbacks = cb;
    }
    return 0;
}

int ble_transport_svc_send(struct bt_conn *conn, const uint8_t *data, uint16_t len)
{
    struct bt_gatt_notify_params params = {0};
    const struct bt_gatt_attr *attr = &trans_svc.attrs[2];

    params.attr = attr;
    params.data = data;
    params.len = len;

    if (conn && (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)))
    {
        return bt_gatt_notify_cb(conn, &params);
    }
    else
    {
        return -EINVAL;
    }
}
